/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.aggregate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.ui.basic.PageContainerPanelItem;

/**
 * A <code>PageContainerPanel</code> is a panel which lives inside of a
 * {@link PageContainer}. A <code>PageContainerPanel</code> is a specialised
 * {@link VerticalPanel} tailored to the needs of a <code>PageContainer</code>.
 * 
 * @author Arthur Kalmenson
 */
public class PageContainerPanel extends VerticalPanel {

	/**
	 * The CSS class for a panel item that is not active.
	 */
	private static final String PANEL_ITEM_STYLE = "mshabPanelItem";

	/**
	 * The CSS class for a panel item that is active.
	 */
	private static final String ACTIVE_PANEL_ITEM_STYLE = "mshabActivePanelItem";

	/**
	 * The CSS for the panel's header.
	 */
	private static final String PANEL_HEADER_CSS = "mshabPanelHeader";

	/**
	 * A {@link HashMap} containing a name to {@link Widget} relationship.
	 */
	private HashMap nameToPanelItem;

	/**
	 * The title of this panel.
	 */
	private Label header;

	/**
	 * Stores the clicked widget.
	 */
	public static Widget clicked;

	/**
	 * Create a new <code>PageContainerPanel</code>.
	 */
	public PageContainerPanel() {

		// initialize
		nameToPanelItem = new HashMap();
	}

	/**
	 * Adds the {@link PageContainerPanelItem} <code>item</code> to this
	 * panel.
	 * 
	 * @param item
	 *            the panel item to add.
	 */
	public void add(PageContainerPanelItem item) {

		// if the item already exists (double), do not add it.
		if (nameToPanelItem.get(item.getName()) == null) {
			// add that items component.
			this.insert((Widget) item.getComponent(), item.getIndex());
			nameToPanelItem.put(item.getName(), item);

			// set CSS style
			((Widget) item.getComponent()).setStyleName(PANEL_ITEM_STYLE);

			// add the ClickListener to switch CSS styles
			item.getComponent().addClickListener(new PanelItemListener());
		}
	}

	/**
	 * Add widget to panel <code>PageContainerPanel</code> with a name.
	 * 
	 * @param name
	 *            the name of the {@link Widget} being added.
	 * @param widget
	 *            the {@link Widget} to be added to panel
	 *            <code>PageContainerPanel</code>.
	 */
	public void add(String name, SourcesClickEvents widget) {

		addToPanel(name, widget);
	}

	/**
	 * Insert widget to panel <code>PageContainerPanel</code> in index with a
	 * name.
	 * 
	 * @param name
	 *            the name of the {@link Widget} being added.
	 * @param widget
	 *            the {@link Widget} to be added to panel
	 *            <code>PageContainerPanel</code>.
	 * @param index
	 *            the index where to insert the Widget.
	 */
	public void insert(String name, SourcesClickEvents widget, int index) {
		insertToPanel(name, widget, index);
	}

	/**
	 * Appends a new {@link Hyperlink} that points to <code>historyToken</code>
	 * and has it's link named <code>name</code>.
	 * 
	 * @param name
	 *            the name of the {@link Hyperlink}.
	 * @param historyToken
	 *            where to navigate to.
	 */
	public void add(String name, String historyToken) {
		Hyperlink link = new Hyperlink(name, historyToken);
		addToPanel(name, link);
	}

	/**
	 * Inserts a new {@link Hyperlink} that points to <code>historyToken</code>
	 * and has it's link named <code>name</code> into <code>index</code>.
	 * 
	 * @param name
	 *            the name of the {@link Hyperlink}.
	 * @param historyToken
	 *            where to navigate to.
	 * @param index
	 *            the index where to insert the new hyperlink.
	 */
	public void insert(String name, String historyToken, int index) {
		Hyperlink link = new Hyperlink(name, historyToken);
		insertToPanel(name, link, index);
	}

	/**
	 * Appends a new {@link Hyperlink} that uses <code>name</code> as the link
	 * and the displayed name.
	 * 
	 * @param name
	 *            the name of the {@link Hyperlink} and where it points to.
	 */
	public void add(String name) {
		Hyperlink link = new Hyperlink(name, name);
		link.setStyleName(PANEL_ITEM_STYLE);
		addToPanel(name, link);
	}

	/**
	 * Inserts a new {@link Hyperlink} that uses <code>name</code> as the link
	 * and the displayed name into <code>index</code>.
	 * 
	 * @param name
	 *            the name of the {@link Hyperlink} and where it points to.
	 * @param index
	 *            the index where to insert the new hyperlink.
	 */
	public void insert(String name, int index) {
		Hyperlink link = new Hyperlink(name, name);
		link.setStyleName(PANEL_ITEM_STYLE);
		insertToPanel(name, link, index);

	}

	/**
	 * Adds a widget with name, name, to this <code>PageContainerPanel</code>.
	 * 
	 * @param name
	 *            the name of the widget to add.
	 * @param widget
	 *            the widget being added.
	 */
	private void addToPanel(String name, SourcesClickEvents widget) {

		// if the item already exists (double), do not add it.
		if (nameToPanelItem.get(name) == null) {
			// create a new item.
			PageContainerPanelItem item = new PageContainerPanelItem(name,
					widget, this, this.getWidgetCount(), false);

			// add that items component.
			this.add((Widget) item.getComponent());
			nameToPanelItem.put(name, item);

			// set CSS style
			((Widget) item.getComponent()).setStyleName(PANEL_ITEM_STYLE);
		}
	}

	/**
	 * Inserts a widget with name, name, into this
	 * <code>PageContainerPanel</code> at index.
	 * 
	 * @param name
	 *            the name of the widget to add.
	 * @param widget
	 *            the widget being added.
	 * @param index
	 *            the index where to insert it.
	 */
	private void insertToPanel(String name, SourcesClickEvents widget, int index) {

		// if the item already exists (double), do not add it.
		if (nameToPanelItem.get(name) == null) {
			// create a new item.
			PageContainerPanelItem item = new PageContainerPanelItem(name,
					widget, this, index, false);

			// add that items component.
			this.insert((Widget) item.getComponent(), index);
			nameToPanelItem.put(name, item);

			// set CSS style
			((Widget) item.getComponent()).setStyleName(PANEL_ITEM_STYLE);
		}
	}

	/**
	 * Returns the Widget with the name, <code>name</code>.
	 * 
	 * @param name
	 *            the name of the Widget to return.
	 * @return the Widget with the name, <code>name</code>.
	 */
	public Widget get(String name) {
		PageContainerPanelItem item = (PageContainerPanelItem) nameToPanelItem
				.get(name);
		return (Widget) item.getComponent();
	}

	/**
	 * Returns the {@link PageContainerPanelItem} associated with
	 * <code>name</code>.
	 * 
	 * @param name
	 *            the name of the {@link PageContainerPanelItem} we want.
	 * @return the {@link PageContainerPanelItem} associated with
	 *         <code>name</code>.
	 */
	public PageContainerPanelItem getItem(String name) {
		return (PageContainerPanelItem) nameToPanelItem.get(name);
	}

	/**
	 * Removes the {@link Widget} from panel who's display name is
	 * <code>displayName</code>.
	 * 
	 * @param displayName
	 *            the name of the <code>Hyperlink</code> to remove.
	 */
	public void remove(String name) {

		PageContainerPanelItem item = (PageContainerPanelItem) nameToPanelItem
				.get(name);

		// if the item isn't null, remove its component
		if (item != null) {

			this.remove((Widget) item.getComponent());

			// remove the widget from the HashMap
			nameToPanelItem.remove(name);
		}
	}

	/**
	 * Removes all the Page level {@link PageContainerPanelItem} from this panel.
	 */
	public void removePageLevelItems() {

		// need to store the names of
		ArrayList toRemove = getPageLevelItems();
		for (int i = 0; i < toRemove.size(); i++) {
			PageContainerPanelItem item = (PageContainerPanelItem) toRemove
					.get(i);
			remove(item.getName());
		}
	}

	/**
	 * Returns an {@link ArrayList} of all the Page level
	 * {@link PageContainerPanelItem} in this panel.
	 * 
	 * @return an {@link ArrayList} of all the Page level
	 * {@link PageContainerPanelItem} in this panel.
	 */
	public ArrayList getPageLevelItems() {

		ArrayList result = new ArrayList();
		Iterator iter = nameToPanelItem.keySet().iterator();
		while (iter.hasNext()) {
			PageContainerPanelItem item = (PageContainerPanelItem) nameToPanelItem
					.get(iter.next());
			if (item.isPageLevel()) {
				result.add(item);
			}
		}

		return result;
	}

	/**
	 * Adds a header to this panel.
	 * 
	 * @param header
	 *            the text of the header being added.
	 */
	public void addHeader(String header) {
		setHeader(header);
	}

	/**
	 * Sets the text of the header of this panel to the provided String.
	 * 
	 * @param header
	 *            the new header.
	 */
	public void setHeader(String header) {
		this.header = new Label(header);
		this.header.setStyleName(PANEL_HEADER_CSS);
		this.insert(this.header, 0);
	}

	/**
	 * Removes the header of this panel.
	 */
	public void removeHeader() {
		if (header != null) {
			this.remove(header);
			header = null;
		}
	}

	/**
	 * Returns the text of the header associated with this panel.
	 * 
	 * @return the text of the header associated with this panel.
	 */
	public String getHeader() {
		String titleString = null;
		if (header != null) {
			titleString = header.getText();
		}
		return titleString;
	}

	/**
	 * Makes the panel item with name, <code>name</code>, active.
	 * 
	 * @param name
	 *            the panel to make active.
	 */
	public void makeActive(String name) {
		PageContainerPanelItem item = (PageContainerPanelItem) nameToPanelItem
				.get(name);

		if (item != null) {
			Widget w = (Widget) item.getComponent();
			w.setStyleName(ACTIVE_PANEL_ITEM_STYLE);
		}
	}

	/**
	 * Activates the <code>clicked</code> widget.
	 */
	public void activateClickedWidget() {
		if (clicked != null) {
			clicked.setStyleName(ACTIVE_PANEL_ITEM_STYLE);
		}
	}

	/**
	 * Removes the active CSS style from every item in this panel.
	 */
	public void clearActiveCSS() {
		for (int i = 0; i < this.getWidgetCount(); i++) {
			this.getWidget(i).setStyleName(PANEL_ITEM_STYLE);
		}
	}

	public void clear() {
		super.clear();
		nameToPanelItem.clear();
	}

	/**
	 * A <code>PanelItemListener</code> listens for when a panel item is
	 * clicked and sets the appropriate CSS styles.
	 * 
	 * @author Arthur Kalmenson
	 */
	private class PanelItemListener implements ClickListener {

		public void onClick(Widget sender) {

			// remove the CSS style of every panel item
			clearActiveCSS();

			// add active CSS to the sender.
			clicked = sender;
		}
	}
}
